/*
	TODO
	如果中途断开SIM卡,ppp连接不会消失,这个时候需要上层在kill pppd 即可
*/




#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
#include <errno.h>
#include <fcntl.h> 
#include <termios.h> 
#include <pthread.h>

#define CMD_CHECK_CONNECT   "AT"
#define CMD_ATE_OPEN        "ATE1"
#define CMD_ATE_CLOSE       "ATE0"
#define CMD_CMD_REP         "A/"
#define CMD_VOLT_REQUEST    "AT+CBC"
#define CMD_SIM_EXIST       "AT+CPIN?"
#define CMD_SIM_TYPE        "AT+COPS?"
#define CMD_SIM_SIG_STREN   "AT+CSQ"
#define CMD_SIM_TEL_NUM     "AT+CNUM"
#define CMD_SIM_MANF        "AT+CGMM"
#define CMD_SIM_MODULE      "ATI"
#define CMD_SIM_FIRM        "AT+GMR"
#define CMD_SIM_SERI        "AT+CGSN"
#define CMD_TELEPHONE_OFF   "ATH"
#define CMD_TELEPHONE_ON    "ATA"
#define CMD_ATCVHU          "AT+CVHU=0"
#define CMD_MSG_TMODE       "AT+CMGF=1"
#define CMD_MSG_CODEGSM     "AT+CSCS=\"GSM\""
#define CMD_MSG_REC_UNREAD  "AT+CMGL=\"REC UNREAD\""
#define CMD_MSG             "AT+CMGS="
#define CMD_MSG_READ        "AT+CMGR="
#define CMD_NET_STAT        "AT+CREG?"

int PPPDevInit(const char *devName, int baud)
{
	int fd = -1;
	struct termios newio;
	struct termios oldio;
	
	fd = open(devName, O_RDWR | O_NOCTTY);
	if (fd < 0)
	{
		printf("open tty fail. %s\n", strerror(errno));
		return -1;
	}

	memset(&oldio, 0, sizeof(oldio));
	if (0 != tcgetattr(fd, &oldio))
	{
		printf( "tcgetattr error. %s\n", strerror(errno));
		return -1;
	}

	memset(&newio, 0, sizeof(newio));
	newio.c_cflag |= CLOCAL | CREAD;
	newio.c_cflag &= ~CSIZE;
	newio.c_cflag |= CS8;  //8比特数据位
	newio.c_cflag &= ~PARENB; //无奇偶校验位
	newio.c_cflag &= ~CSTOPB; //1个停止位
	newio.c_cc[VTIME]  = 0;
	newio.c_cc[VMIN]  = 0;
	switch(baud)
	{
		case 110:
			cfsetispeed(&newio, B110);
			cfsetospeed(&newio, B110);
			break;
		case 300:
			cfsetispeed(&newio, B300);
			cfsetospeed(&newio, B300);
			break;
		case 600:
			cfsetispeed(&newio, B600);
			cfsetospeed(&newio, B600);
			break;
		case 1200:
			cfsetispeed(&newio, B1200);
			cfsetospeed(&newio, B1200);
			break;
		case 2400:
			cfsetispeed(&newio, B2400);
			cfsetospeed(&newio, B2400);
			break;
		case 4800:
			cfsetispeed(&newio, B4800);
			cfsetospeed(&newio, B4800);
			break;
		case 9600:
			cfsetispeed(&newio, B9600);
			cfsetospeed(&newio, B9600);
			break;
		case 19200:
			cfsetispeed(&newio, B19200);
			cfsetospeed(&newio, B19200);
			break;
		case 38400:
			cfsetispeed(&newio, B38400);
			cfsetospeed(&newio, B38400);
			break;
		case 57600:
			cfsetispeed(&newio, B57600);
			cfsetospeed(&newio, B57600);
			break;
		case 115200:
			cfsetispeed(&newio, B115200);
			cfsetospeed(&newio, B115200);
			break;
		case 230400:
			cfsetispeed(&newio, B230400);
			cfsetospeed(&newio, B230400);
			break;
		case 460800:
			cfsetispeed(&newio, B460800);
			cfsetospeed(&newio, B460800);
			break;
		default:
			cfsetispeed(&newio, B115200);
			cfsetospeed(&newio, B115200);
			break;
	}

	/* 清空缓存 */
	tcflush(fd, TCIFLUSH);
	tcflush(fd, TCOFLUSH);

	if (0 != tcsetattr(fd, TCSANOW, &newio))
	{
		printf( "tcsetarrt error. %s\n", strerror(errno));
		return -1;
	}
	printf( "%s init ok\n", devName);
	return fd;
}

static int read_data(int fd, char *buff, int len)
{
	int ret;
	ret = read(fd,buff,len);
	if(ret < 0) {
		perror("read_data");
		return -1;
	}
	buff[ret] = '\0';
	return ret;
}

/*********************************************************************************************************
** Function name:           write_data
** Descriptions:            串口数据读取。
** input parameters:        uart串口操作结构体；len字符串长度。buff输入字符串缓存。
** output parameters:       无
** Returned value:          0--成功，其他--失败
** Created By:              sok
** Created date:            2015.1.6
*********************************************************************************************************/
static int write_data(int fd,const char *buff,int len)
{
    int ret;
	char buf[100];
	
	strcpy(buf, buff);
	ret = strlen(buf);
	if((buf[ret-1] != '\n') && ret > 1) {
		buf[ret] = '\r';
		buf[ret+1] = '\n';
		buf[ret+2] = '\0';

	} else if(ret <= 1) {
		return -2;
	}
        ret = write(fd,buf,strlen(buf));
        if(ret < 0) {
                perror("read_data");
                return -1;
        }
        return ret;
}


#define WAIT_SHORT	1
#define WAIT_LONG	60*2	//AT+COPS=?

int PppAtCheck(int fd, const char *cmd, char *ackBuf, int ackBufLen,unsigned char  wait_time)
{
	int ret = -1;
	memset(ackBuf, 0, ackBufLen);
	if (NULL == cmd || NULL == ackBuf ||  ackBufLen < 0)
	{
		printf("%s%d param error.\n", __FUNCTION__, __LINE__);
		return -1;
	}
	
	ret = write_data(fd,cmd, strlen(cmd));
	if (ret < 0)
	{
		printf("%s%d write error. cmd = %s\n", __FUNCTION__, __LINE__, cmd);
		return -1;
	}
	sleep(wait_time);
	ret = read_data(fd,ackBuf, ackBufLen);
	if (ret < 0)
	{
		printf("%s%d write error. ret = %d\n", __FUNCTION__, __LINE__, ret);
		return -1;
	}
	return ret;
	
}
#define ISP_NAME_LEN 20
char isp_name[ISP_NAME_LEN];

int AtCheckHardware(int fd)
{
	char ackbuf[128]={0};
	const char *delim = "\r\n";
	char *saveptr = NULL;
	char *str = NULL;
	int csq = -1;
	
	int faild_try=5;
	while(faild_try--)
	{
		printf("Check 2G/3G/4G moulde\n");
		memset(isp_name,0,sizeof(isp_name));
		// 检测SIM卡是否存在
		if(PppAtCheck(fd, CMD_SIM_EXIST, ackbuf, sizeof(ackbuf),WAIT_SHORT))
		{
			if (NULL != strstr(ackbuf, "READY"))
			{
				printf("SIM EXIST OK\n");
				// 检测信号强度
				if(PppAtCheck(fd, CMD_SIM_SIG_STREN, ackbuf, sizeof(ackbuf),WAIT_SHORT))
				{
					char csqbuf[32];
					str = strtok_r(ackbuf, delim, &saveptr);
					while(str)
					{
						if (strstr(str, "CSQ:"))
						{
							memset(csqbuf, 0, sizeof(csqbuf));
							strncpy(csqbuf, &str[6], 2);
							csq = atoi(csqbuf);
							break;
						}
						str = strtok_r(NULL, delim, &saveptr);
					}
					if(csq>=10 && csq<99)
					{
						printf("SIG=%2d OK\n",csq);
						// 查询注册到网络
						if(PppAtCheck(fd, CMD_NET_STAT, ackbuf, sizeof(ackbuf),WAIT_SHORT))
						{
							int  creg = 0;
							str = strtok_r(ackbuf, delim, &saveptr);
							while(str)
							{
								if (strstr(str, "CREG:"))
								{
									char cregbuf[16];
									memset(cregbuf, 0, sizeof(cregbuf));
									strncpy(cregbuf, &str[9], 2);
									creg = atoi(cregbuf);
									break;
								}

								str = strtok_r(NULL, delim, &saveptr);
							}
							//1 Registered, home network
							//5 Registered, roaming 漫游 
							if ((1 == creg) || (5 == creg))
							{
								// 查询运营商,后续可以自动寻找APN从文件读取
								if(PppAtCheck(fd, CMD_SIM_TYPE, ackbuf, sizeof(ackbuf),WAIT_SHORT))
								{
									str = strtok_r(ackbuf, delim, &saveptr);
									while(str)
									{
										if (strstr(str, "+COPS:"))
										{
											memset(isp_name,0,sizeof(isp_name));
											int fst=0,i=0,j=0;
											for(;i<strlen(str),j<sizeof(isp_name)-1;i++)
											{
												if(str[i]=='\"')
												{
													if(fst==0) {fst=1;continue;}
													else break;
												}
												if(fst==1)
												{
													isp_name[j++]=str[i];	
												}
											}
										}
										str = strtok_r(NULL, delim, &saveptr);
									}
									if(strlen(isp_name)>0)
									break;
								}
							}
						}	
					}
				}
			}
		}
	}
	printf("ISP NAME:%s\n",isp_name);
	return csq;
}
int c_netlink_status(const char *if_name )
{
	int fd = -1; 
	struct ifreq ifr; 
	struct ifconf ifc;  
	struct ifreq ifrs_buf[100]; 
	int if_number =0;
	int i;
 
	if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
	{
		fprintf(stderr, "%s: socket error [%d] %s\r\n",if_name, errno, strerror(errno));
		close(fd);
		return -1; 
	}
 
	ifc.ifc_len = sizeof(ifrs_buf);  
	ifc.ifc_buf = (caddr_t)ifrs_buf;  
	if (ioctl(fd, SIOCGIFCONF, (char *)&ifc) <0)  
	{
		fprintf(stderr, "%s: ioctl SIOCGIFCONF error [%d] %s\r\n",if_name, errno, strerror(errno));
		close(fd);
		return -1; 
	}
 
	if_number = ifc.ifc_len / sizeof(struct ifreq);
	for(i=0; i< if_number; i++)
	{
		if(strcmp(if_name,ifrs_buf[i].ifr_name ) == 0)
		{
			break;
		}
	}
	if(i >= if_number)
	{
		close(fd);
		//fprintf(stderr, "DEVICE_NONE\r\n");
		return 0;
	}
	
	bzero(&ifr, sizeof(ifr));
	strncpy(ifr.ifr_name, if_name, IFNAMSIZ-1); 
	ifr.ifr_name[IFNAMSIZ-1] = 0; 
	if (ioctl(fd, SIOCGIFFLAGS, (char *)&ifr) <0)  
	{
		//fprintf(stderr, "%s: ioctl SIOCGIFFLAGS error [%d] %s\r\n",if_name, errno, strerror(errno));
		close(fd);
		return -1; 
	}	
	if(!(ifr.ifr_flags & IFF_UP))
	{
		close(fd);
		//fprintf(stderr, "DEVICE_DOWN\r\n");
		return 1;
	}
	if(!(ifr.ifr_flags & IFF_RUNNING))
	{
		close(fd);
		//fprintf(stderr, "DEVICE_UNPLUGGED\r\n");
		return 2 ;
	}
	//printf("%s Linked\r\n",if_name);
	return 3;
}



static char ppp_apn[100];
static char ppp_username[100];
static char ppp_password[100];
static char ppp_tel[100];
static char GET_DC_ROOT[100];
static char SCRIPT_PPP_RST[100];
const char SCRIPT_PPP_ON[]  = "/ppp/ppp-on";
const char _SCRIPT_PPP_RST[]  = "/ppp/ppp-dev-rst.sh";


static void setPPPParameters()
{
    snprintf(ppp_apn,sizeof(ppp_apn)-1,"%s",getenv("APN"));
    snprintf(ppp_username,sizeof(ppp_username)-1,"%s",getenv("PPP_USER"));
    snprintf(ppp_password,sizeof(ppp_password)-1,"%s",getenv("PPP_PASSWORD"));
    snprintf(ppp_tel,sizeof(ppp_tel)-1,"%s",getenv("PPP_TEL"));
    snprintf(GET_DC_ROOT,sizeof(GET_DC_ROOT)-1,"%s",getenv("DC_ROOT"));
	snprintf(SCRIPT_PPP_RST, sizeof(SCRIPT_PPP_RST), "%s%s",GET_DC_ROOT,_SCRIPT_PPP_RST);
}


static void callPPP_ON()
{
	char szTmp[128];
	snprintf(szTmp, sizeof(szTmp), "%s%s %s %s %s %s &",
			GET_DC_ROOT,
			SCRIPT_PPP_ON,
			ppp_apn,
			ppp_username,ppp_password,ppp_tel);
	//system("ps -ef |grep pppd |awk '{print $1}'|xargs kill -9");
	system("killall  pppd");
	system(szTmp);
}


void *pppThreadRunner(void *arg)
{
	int ret = -1;
	int fd = -1;

	setPPPParameters();
	fd = PPPDevInit("/dev/ttymxc5", 115200);
	if (fd < 0)
	{
		printf("InitTtyx fail.\n");
		return ;
	}
	int ppp_exist=(c_netlink_status("ppp0")==3?1:0);
	int try_num=0;
	while(1)
	{
		ppp_exist=(c_netlink_status("ppp0")==3?1:0);
		if(ppp_exist)
		{	
			try_num=0;
			sleep(60);
			continue;
		}
		if(AtCheckHardware(fd)>0)
		{
			callPPP_ON();
			try_num++;
		}
		else
		{
			system(SCRIPT_PPP_RST);
		}
		sleep(10);
	}
}

int main() {
	pppThreadRunner(0);
    return 0;
}